Skip to content

refactor(dfns): better representation for component relations - #417

Draft
wpbonelli wants to merge 11 commits into
MODFLOW-ORG:developfrom
wpbonelli:binding-params
Draft

wpbonelli wants to merge 11 commits into
MODFLOW-ORG:developfrom
wpbonelli:binding-params

Conversation

@wpbonelli

Copy link
Copy Markdown
Member

Rethink #406, #407. The basic problem is that MF6 components refer to each other in various ways:

  • Hierarchical or not. A simulation has models, a model has packages. But exchanges and solutions point at models, which are all siblings under the simulation.
  • Fixed by the spec or by the input. A model's parent is always the simulation. But the input decides which models an exchange or solution points to, and which package a TS or OBS file belongs to.
  • Where the info defining the relationship lives. Usually it's in one file: an exchange's link to its models is in mfsim.nam EXCHANGES, and that's it. But some reference info is split across two files, e.g.
    • exchange cell IDs are in the model exchange file, the models are named in mfsim.nam
    • SPC bndno numbers the bounds of a flow package, and only SSM says which one

The spec/schema has been accruing a bunch of ad hoc machinery to represent all this, no bueno. There is theory we can lean on, of course it comes from Knuth: attribute grammars. An attribute grammar attaches information to the nodes of a tree, so you can define semantics in a controlled way. But that's a bit abstract, so probably best reserved for internal docs. Rewrite the DFN spec and schema in analogous but more familiar concepts

  • Bindings name subcomponents' input files and define a simulation's tree structure.
  • References name something elsewhere in the tree, and resolve against the tree.
  • Parameters declare information a component's references need from its parent.
  • Arguments are what a parent passes to a child's parameters, through a binding.

This can all be thought of as a function call, where parametrized components let you send information through bindings down the tree.

To make this concrete, a child DFN (e.g. exchange) can declare a "signature" which a parent DFN (e.g. simulation) can "call". So in mfsim.nam exchanges block

GWF6-GWF6  model.gwfgwf  gwf_1  gwf_2
           ^ binding     ^ reference to a model, "passed" to the exchange

A parameter is passed along a binding, from parent to child, but what it describes can point anywhere the parent can see: e.g. gwf_1 is the exchange's sibling, not its ancestor. This is like lexical scoping: references resolve against the component itself, its parameters, its ancestors (anything above it), or a simulation-scoped singleton package like TDIS. So a parameter is a way to move a non-hierarchical reference down a hierarchical edge. A child can point sideways using a name resolved by its parent.

wpbonelli and others added 11 commits November 11, 2026 09:51
A component declares what it needs from whatever binds its file, in
`params`: an exchange's two models (from sim-nam's exchanges row), a
mover's or GNC's (passed down from the exchange), SPC's and SPCA's flow
package (from SSM's row), and OBS's target (its parent). An fk names one
with `param`, a cellid's `model` names one instead of "1"/"2", and a
`when` may test one (MVR's MODELNAMES is set exactly when model2 is).

The rules exchange_models and ssm_package go; no_advanced_transport
takes the part of ssm_package that isn't about the binding. OBS's
obstype names one of its target's observation types, under the
reserved key set name `observations`.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
A component's `params` is now a signature, `{type, rule}`, and the file
that binds it passes them in `args`, each naming a column of the binding
row or one of the binder's own parameters. Drops `from` and its three
source forms. Every exchange declares `model1`/`model2`.

OBS needs no parameter: its target is the file's owner, which
cross-component fks already resolve against. Drop obstype's fk and the
reserved `observations` key set with it, and the fk form that picked a
key's instance by a rule alone, which nothing uses now.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Adds a Bindings section: terms (binding, parent, binding row, reference,
context), every kind of binding in MODFLOW 6 with its input, and
parameters as the cases that need them. Moves Parameters into it, and
uses the same terms in the schema docstring and migration comments.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Scope here is the programming sense, and so are the spec's other uses:
a dimension's scope says whose scope it's in, and a scoped key is a
qualified name.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Bindings build the tree that references resolve in, so the Bindings
section now comes before Primary and foreign keys, and Parameters,
which uses keys, key sets and rules, is its own section after Rules.
Terms says how a binding differs from a reference, with the exchanges
row as one that holds both. The parent and component attributes now
say what a binding is.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
"Binding row" read as a row whose job is binding, but the binding is
one field among keys, references and data: the exchanges row also
refers to its two models. An argument names a sibling of the binding
with an fk. The validator's message says "sibling column" to match.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The spec said no binding knows a transport model's flow model, since no
exchange binds SSM. But mfsim.nam binds the transport model and holds
the pairing, in an exchanges row rather than the binding's siblings. An
argument can name only a sibling or a parent's parameter, which is why
flow_model is a rule.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
A binding is a field in the parent's input naming a subcomponent's input
file. It introduces what it names; a reference names something that exists
without it, and resolves by lookup in the tree.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Terms now defines parameters and arguments. Scope lists sim-tdis alone:
sim-nam is every component's ancestor already, and no fk targets
sim-tdis; it's in scope for its nper dimension.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@wpbonelli wpbonelli added this to the 1.10.0 milestone Nov 11, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant